/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_topology.c                                  */
/*  NOTE      =                                                    */
/*  DATE      = 2015/01/21 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <string.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"
#include "conntrack.h"
#include "frct_comm.h"
#include "frct_hash.h"
#include "frct_mng.h"
#include "frstat_topology.h"
#include "frcmd_server.h"

#define  STAT_TOPOLOGY_HASH_SIZE    100
#define  STAT_TOPOLOGY_IP_HASH_SIZE 10240

typedef struct __frstat_show {
    u_int   ui_dip;
    u_short ui_dport;
    int     i_l7_proto;
    void   *i_socketid;
    
} Frstat_show;

typedef struct __ip_list {
    u_int ui_ip;
    
} Frip_list;

typedef struct __topology {
    u_int   ui_dip;
    u_short us_dport;
    int     i_l7_proto;
    
    Frhash_header *p_st_hash_ip;

} Frstat_topology;

/*******************************************************************************
*  FUNC     :  释放节点内容
*  ARGS     :  p_node 
*  RTN      :
*  NOTE     :  
*******************************************************************************/
FR_STATIC void __ip_node_free(Frlist_node* p_node)
{
    Frip_list *p_stat_node = NULL;
    
    if (p_node && p_node->data)
    {
        p_stat_node = (Frip_list *)p_node->data;
        
        frfree(p_stat_node);
    }
}

/*******************************************************************************
*  FUNC     :  释放节点内容
*  ARGS     :  p_node 
*  RTN      :
*  NOTE     :  
*******************************************************************************/
FR_STATIC void __topology_node_free(Frlist_node* p_node)
{
    Frstat_topology *p_stat_node = NULL;
    
    if (p_node && p_node->data)
    {
        p_stat_node = (Frstat_topology *)p_node->data;
        
        frhash2_free2(p_stat_node->p_st_hash_ip, __ip_node_free);
        frfree(p_stat_node);
    }
}

/*******************************************************************************
*  FUNC     :  组成哈希用key
*  ARGS     :  i_l7_proto   (l7协议名)
*           :  ui_ip        (ip地址)
*           :  us_port      (端口)
*           :  p_key        (保存地址)
*           :  ui_key_size  (保存地址长度)
*  RTN      :  key长度
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frhash_make_key(int i_l7_proto, u_int ui_ip, u_short us_port
                              , char *p_key, unsigned int ui_key_size)
{
    int i_idx=0;
    int *stupid_strict_aliasing_warnings=(int*)p_key;

    if(p_key == NULL || ui_key_size <= sizeof(u_int) + sizeof(int) + sizeof(u_short))
    {
        FRLOG_ERROR_DETAIL("expect hash key err");
        return 0;
    }
    *stupid_strict_aliasing_warnings = i_l7_proto;
    i_idx += sizeof(int);
    *(u_int *) (p_key + i_idx) = ui_ip;
    i_idx += sizeof(u_int);
    *(u_short *) (p_key + i_idx) = us_port;
    i_idx += sizeof(u_short);
    
    return i_idx;
}

/*******************************************************************************
*  FUNC     :  添加应用拓扑信息
*  ARGS     :  p_st_list_header     (hash头指针)
*           :  ui_sip               (源ip地址)
*           :  ui_dip               (目的ip地址)
*           :  us_dport             (目的端口)
*           :  i_l7_proto           (l7协议名)
*  RTN      :  FR_SUCC (success)
*           :  FR_FAIL (failed)
*  NOTE     :
*******************************************************************************/
FR_STATIC int frstat_topology_add(Frhash_header *p_st_hash_hdr
                                , u_int ui_sip, u_int ui_dip, u_short us_dport
                                , int i_l7_proto)
{
    Frlist_node *p_st_node = NULL;
    Frlist_node *p_st_new_node = NULL;
    Frlist_node *p_st_new_ip_node = NULL;
    Frstat_topology *p_st_new_topology = NULL;
    Frip_list *p_st_new_iplist = NULL;
    char arr_hash_key[25] = {'\0'};
    int i_hash_key_len = 0;
    
    if(p_st_hash_hdr == NULL)
        return FR_FAIL;

    i_hash_key_len = __frhash_make_key(i_l7_proto, ui_dip, us_dport, arr_hash_key, sizeof(arr_hash_key));
    if (i_hash_key_len == 0)
        return FR_FAIL;
    
    p_st_node = frhash2_getStr(p_st_hash_hdr, arr_hash_key, i_hash_key_len);
    if (p_st_node && p_st_node->data)
    {
        Frstat_topology *p_st_stat = (Frstat_topology *)p_st_node->data;
        Frlist_node *p_st_ip_node = NULL;
        
        p_st_ip_node = frhash2_getLong(p_st_stat->p_st_hash_ip, ui_sip);
        if (p_st_ip_node)
            return FR_SUCC;
        //添加一个节点
        p_st_new_iplist = (Frip_list *) frmalloc(sizeof(Frip_list));
        if (p_st_new_iplist == NULL)
            return FR_FAIL;
        memset(p_st_new_iplist, 0x00, sizeof(Frip_list));
        p_st_new_iplist->ui_ip = ui_sip;
        p_st_new_node = frlist_node_create(p_st_new_iplist);
        if (p_st_new_node == NULL)
        {
            frfree(p_st_new_iplist);
            return FR_FAIL;
        }
        frhash2_addLong(p_st_stat->p_st_hash_ip, ui_sip, p_st_new_node);
        
        return FR_SUCC;
    }
    p_st_new_topology = (Frstat_topology *) frmalloc(sizeof(Frstat_topology));
    if (p_st_new_topology == NULL)
        return FR_FAIL;
    memset(p_st_new_topology, 0x00, sizeof(Frstat_topology));
    p_st_new_topology->ui_dip = ui_dip;
    p_st_new_topology->us_dport = us_dport;
    p_st_new_topology->i_l7_proto = i_l7_proto;
    p_st_new_node = frlist_node_create(p_st_new_topology);
    if (p_st_new_node == NULL)
    {
        frfree(p_st_new_topology);
        return FR_FAIL;
    }
    //添加iplist节点
    p_st_new_topology->p_st_hash_ip = frhash2_create2(STAT_TOPOLOGY_IP_HASH_SIZE);
    if (p_st_new_topology->p_st_hash_ip == NULL)
    {
        __topology_node_free(p_st_new_node);
        return FR_FAIL;
    }
    p_st_new_iplist = (Frip_list *) frmalloc(sizeof(Frip_list));
    if (p_st_new_iplist == NULL)
    {
        __topology_node_free(p_st_new_node);
        return FR_FAIL;
    }
    memset(p_st_new_iplist, 0x00, sizeof(Frip_list));
    p_st_new_iplist->ui_ip = ui_sip;
    p_st_new_ip_node = frlist_node_create(p_st_new_iplist);
    if (p_st_new_ip_node == NULL)
    {
        frfree(p_st_new_iplist);
        __topology_node_free(p_st_new_node);
        return FR_FAIL;
    }
    frhash2_addLong(p_st_new_topology->p_st_hash_ip, ui_sip, p_st_new_ip_node);
    
    frhash2_addStr(p_st_hash_hdr, arr_hash_key, i_hash_key_len, p_st_new_node);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  根据源ip hash打印拓扑关系
*  ARGS     :  p_node       (当前节点)
*           :  p_data       (节点的信息)
*  RTN      :  1   (继续运行)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __topology_ip_show(Frlist_node *p_node, const void *p_data)
{
    Frstat_show *p_stat_show = (Frstat_show *)p_data;
    Frip_list *p_ip_list = NULL;
    char arr_ip_src[56], arr_ip_dst[56];
    char arr_show[128];
    
    if (p_node==NULL || p_node->data == NULL || p_data==NULL)
        return 1;

    p_ip_list = (Frip_list *) p_node->data;
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_ip_list->ui_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_dst), "%s", int_ntoa(p_stat_show->ui_dip));
    snprintf(arr_show, sizeof(arr_show), "L7=%d src=%s dst=%s dport=%d\n"
           , p_stat_show->i_l7_proto, arr_ip_src, arr_ip_dst, p_stat_show->ui_dport);

    frzmq_send_msg(&p_stat_show->i_socketid, arr_show, strlen(arr_show), 0);
    
    return 1;
}

/*******************************************************************************
*  FUNC     :  根据总hash打印拓扑关系
*  ARGS     :  p_node       (当前节点)
*           :  p_data       (节点的信息)
*  RTN      :  1   (继续运行)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __topology_stat_show(Frlist_node* p_node, const void *p_data)
{
    Frstat_topology *p_stat_node = (Frstat_topology *)p_node->data;
    Frstat_show st_stat_show;
    
    st_stat_show.ui_dip = p_stat_node->ui_dip;
    st_stat_show.ui_dport = p_stat_node->us_dport;
    st_stat_show.i_l7_proto = p_stat_node->i_l7_proto;
    st_stat_show.i_socketid = (void *) p_data;
    
    frhash2_run(p_stat_node->p_st_hash_ip, &st_stat_show, __topology_ip_show);
    
    return 1;
}

/*******************************************************************************
*  FUNC     :  打印所有的拓扑关系
*  ARGS     :  p_st_list_header     (链表头节点)
*           :  i_socket_id          (发送的socketid)
*  RTN      :  
*  NOTE     :
*******************************************************************************/
FR_STATIC void frstat_topology_output(Frhash_header *p_st_list_header, void *socket_id)
{
    frhash2_run(p_st_list_header, socket_id, __topology_stat_show);
    
}

/*******************************************************************************
*  FUNC     :  释放应用拓扑关系的hash表
*  ARGS     :  p_st_list_header     需要释放的hash表头
*  RTN      :  
*  NOTE     :
*******************************************************************************/
FR_STATIC void frstat_topology_destroy(Frhash_header *p_st_list_header)
{
    frhash2_free(p_st_list_header, __topology_node_free);
}

/*******************************************************************************
*  FUNC     :  run用函数
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_data          (运算用数据)
*  RTN      :  返回值为0时, 终止运行
*  NOTE     : //todo
*******************************************************************************/
FR_STATIC int __hash_node_ct_run(Frlist_node* p_st_node, const void *p_data)
{
    Conntrack *p_st_ct = NULL;
    Frhash_header *p_st_hash_topology = (Frhash_header *)p_data;

    if(p_st_node == NULL || p_st_node->data == NULL || p_data == NULL )
        return 1;

    p_st_ct = (Conntrack *)(p_st_node->data);

    //防止协议查询过程中，有协议释放
    pthread_spin_lock(&p_st_ct->lock);
    if(p_st_ct->st_l7_analyzer.p_st_l7_module == NULL)
    {
        pthread_spin_unlock(&p_st_ct->lock);
        return 1;
    }

    frstat_topology_add(p_st_hash_topology
                      , p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->dst_port
                      , p_st_ct->st_l7_analyzer.p_st_l7_module->i_l7_proto);

    pthread_spin_unlock(&p_st_ct->lock);

    return 1;
}


/*******************************************************************************
*  FUNC     :  topology信息出力
*  ARGS     :  socket_id          通信socket
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :
*******************************************************************************/
int  frstat_topology_report(void *socket_id)
{
    Frhash_header st_hash_topology;

    frhash2_create(&st_hash_topology, STAT_TOPOLOGY_HASH_SIZE);

    frct_del_disable();
    frct_hash_run( &st_hash_topology, __hash_node_ct_run);
    frct_del_enable();
    
    frstat_topology_output(&st_hash_topology, socket_id);
    
    frstat_topology_destroy(&st_hash_topology);
    return FR_SUCC;
}
